package com.leyou.auth.service.service.impl;

import com.leyou.auth.inter.pojo.UserInfo;
import com.leyou.auth.inter.util.JwtUtils;
import com.leyou.auth.inter.util.TokenState;
import com.leyou.auth.service.config.JwtProperties;
import com.leyou.auth.service.service.AuthService;
import com.leyou.common.bean.Constant;
import com.leyou.common.bean.Result;
import com.leyou.common.bean.ResultCode;
import com.leyou.common.util.JsonUtils;
import com.leyou.common.util.StringUtil;
import com.leyou.user.inter.api.IAuthorizeService;
import com.leyou.user.inter.api.IUserService;
import com.leyou.user.inter.dto.UserLoginDTO;
import com.leyou.user.inter.pojo.Role;
import com.leyou.user.inter.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author zqq
 * @ClassName AuthServiceImpl
 * @Description
 * @date 2020/3/4-17:57
 */
@Service
@Slf4j
public class AuthServiceImpl implements AuthService {

    @Autowired
    private IUserService iUserService;

    @Autowired
    private JwtProperties properties;

    @Autowired
    private IAuthorizeService iAuthorizeService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    public Result<String> authentication(UserLoginDTO dto) {
        // 调用微服务，执行查询
        Result<User> userResult = this.iUserService.queryUser(dto);
        if(!userResult.success()){
            return Result.newFailure(userResult.getCode(),userResult.getMessage());
        }
        User user = userResult.getData();
        if(!dto.getFlag() && user.getIsFlag()){
            return Result.newFailure(ResultCode.USER_NOT_AUTHORIZE_FAIL);
        }
        Result<List<Role>> listResult = iAuthorizeService.queryRoleByUid(user.getId());
        if(!listResult.success()){
            return Result.newFailure(listResult.getCode(),listResult.getMessage());
        }
        if(listResult.getData() == null || listResult.getData().size() == 0){
            return Result.newFailure(ResultCode.S_SYSTEM_DATA_EXCEPTION);
        }
        List<String> roleList = new ArrayList<>();
        for (Role role : listResult.getData()) {
            roleList.add(role.getId().toString());
        }
        String token = null;
        try {
            // 如果有查询结果，则生成token
            if(user.getIsFlag()){
                token = JwtUtils.createJWT(new UserInfo(user.getId(), user.getUsername(), JsonUtils.serialize(roleList),dto.getFlag()),
                        properties.getPrivateKey(), properties.getExpire());
            }else{
                token = JwtUtils.createJWT(new UserInfo(user.getId(), user.getUsername(), JsonUtils.serialize(roleList),dto.getFlag()),
                        properties.getPrivateKey(), properties.getWebExpire());
            }
        } catch (Exception e) {
            log.error("生成token异常",e);
            return Result.newFailure(ResultCode.V_GET_TOKEN_FAIL);
        }
        //将token放入redis缓存中
        if(user.getIsFlag()){
            this.redisTemplate.opsForValue().set(Constant.KEY_TOKEN_ID + user.getId(), token, 60L*properties.getExpire(), TimeUnit.SECONDS);
        }else{
            this.redisTemplate.opsForValue().set(Constant.KEY_TOKEN_ID + user.getId(), token, 60L*properties.getWebExpire(), TimeUnit.SECONDS);
        }
        return Result.newSuccess(token);
    }

    @Override
    public Result<UserInfo> verifyUser(HttpServletRequest request, HttpServletResponse response) {
        String token = request.getHeader("x-authorize-token");
        if(!StringUtil.isEmpty(token)){
            log.info("非法访问，未登录");
            return Result.newFailure(ResultCode.S_SYSTEM_VISIT_EXCEPTION);
        }
        Result<UserInfo> result = null;
        UserInfo userInfo = null;
        Map<String, Object> map = JwtUtils.validParseJWT(token, this.properties.getPublicKey());
        switch ((TokenState)map.get("state")){
            case VALID:
                userInfo = (UserInfo) map.get("userInfo");
                String key = Constant.KEY_TOKEN_ID+userInfo.getId();
                String cacheToken = this.redisTemplate.opsForValue().get(key);
                if(!StringUtil.isEmpty(cacheToken)){
                    log.info("token已过期");
                    return Result.newFailure(ResultCode.V_TOKEN_EXP);
                }
                //防止一直持有过期Token访问
                if(!token.equals(cacheToken)){
                    log.info("token已过期");
                    return Result.newFailure(ResultCode.V_TOKEN_EXP);
                }
                //解析成功，刷新token
                try {
                    if(userInfo.getIsFlag()){
                        token = JwtUtils.createJWT(userInfo, this.properties.getPrivateKey(), this.properties.getExpire());
                    }else{
                        token = JwtUtils.createJWT(userInfo, this.properties.getPrivateKey(), this.properties.getWebExpire());
                    }
                } catch (Exception e) {
                    log.error("生成token异常",e);
                    return Result.newFailure(ResultCode.V_GET_TOKEN_FAIL);
                }
                //将token放入redis缓存中
                if(userInfo.getIsFlag()){
                    this.redisTemplate.opsForValue().set(Constant.KEY_TOKEN_ID + userInfo.getId(), token, 60L*this.properties.getExpire(), TimeUnit.SECONDS);
                }else{
                    this.redisTemplate.opsForValue().set(Constant.KEY_TOKEN_ID + userInfo.getId(), token, 60L*this.properties.getWebExpire(), TimeUnit.SECONDS);
                }
                //设置响应头
                response.setHeader("x-authorize-jwt",token);
                response.setHeader("Access-Control-Expose-Headers","x-authorize-jwt");
                result = Result.newSuccess(userInfo);
                break;
            case EXPIRED:
                log.info("token已过期");
                result = Result.newFailure(ResultCode.V_TOKEN_EXP);
                break;
            case SIGNATURE:
                log.info("token签名异常");
                result = Result.newFailure(ResultCode.V_TOKEN_UNAUTHORIZED_FAIL);
                break;
            case ISS_EXCEPTION:
                log.info("token签发者异常");
                result = Result.newFailure(ResultCode.V_TOKEN_UNAUTHORIZED_FAIL);
                break;
            case ANALYSIS_EXCEPTION:
                log.info("token解析异常");
                result = Result.newFailure(ResultCode.V_TOKEN_UNAUTHORIZED_FAIL);
                break;
            default:
                break;
        }
        return result;
    }

    @Override
    public Result logout(HttpServletRequest request) {
        String token = request.getHeader("x-authorize-token");
        if(!StringUtil.isEmpty(token)){
            return Result.newFailure(ResultCode.USER_LOGOUT_FALT);
        }
        Map<String, Object> map = JwtUtils.validParseJWT(token, this.properties.getPublicKey());
        TokenState tokenState = (TokenState)map.get("state");
        if(TokenState.VALID.getState().equals(tokenState.getState())){
            UserInfo userInfo = (UserInfo) map.get("userInfo");
            String key = Constant.KEY_TOKEN_ID+userInfo.getId();
            String cacheToken = this.redisTemplate.opsForValue().get(key);
            if(token.equals(cacheToken)){
                this.redisTemplate.delete(Constant.KEY_TOKEN_ID + userInfo.getId());
                return Result.newSuccess();
            }
        }

        return Result.newFailure(ResultCode.USER_LOGOUT_FALT);
    }
}
